#include "rrquadric.h"
#include "rrtracer.h"
#include <cmath>

RRQuadric::RRQuadric(RRTracer *arg)
{
    parent = arg;
    type = RR_QUADRIC;
}

float RRQuadric::rrHit(RRRay arg)
{
    RRVec3 d = arg.rrGetDir();
    RRVec3 r = arg.rrGetSrc();
    float D;
    A = 1.0f / (size.x * fabsf(size.x));
    B = 1.0f / (size.y * fabsf(size.y));
    C = 1.0f / (size.z * fabsf(size.z));
    float Dx, Dy, Dz, Rx, Ry, Rz;
    Dx = d.x; Dy = d.y; Dz = d.z;
    Rx = r.x - coord.x; Ry = r.y - coord.y; Rz = r.z - coord.z;
    float SecA, SecB, SecC;
    SecB = A * Rx * Dx + B * Ry * Dy + C * Rz * Dz;
    SecA = A * Dx * Dx + B * Dy * Dy + C * Dz * Dz;
    SecC = A * Rx * Rx + B * Ry * Ry + C * Rz * Rz - 1.0f;
    D = SecB * SecB - SecA * SecC;
    if(D >= 0) {
        float t;
        t = (-SecB - sqrtf(D)) / SecA + 1.0e-5;
        if(t > 1.0e-3) {
            RRVec3 v1;
            v1 = RRSetVec3(r.x + t * d.x, r.y + t * d.y, r.z + t * d.z);
            tosrc = RRSetVec3(-t * d.x, -t * d.y, -t * d.z);
            tosrc = RRNormalize(tosrc);
            cross = v1;
            return RRDistance(r, v1);
        }
        else
            return -1.0f;
    }
    return -1.0f;
}

RRVec4 RRQuadric::rrCalcShadow(RRRay arg)
{
    RRLight l;
    l = parent->rrGetLight();
    RRVec3 l_c = l.rrGetCoord();
    float d_l;
    d_l = RRDistance(cross, l_c);

    RRPrim *p;
    float d, dmin;
    int it, index;
    dmin = 1.0e+18;
    it = primset->rrBegin();
    index = -1;
    while(it != primset->rrEnd()) {
        p = primset->rrGetPrim(it);
        d = p->rrHit(arg);
        //printf("distance:%f\n", d);
        if(d < dmin && d > 0.0f) {
            dmin = d;
            index = it;
        }
        it++;
    }

    if(index != -1) {
        if(dmin >= d_l)
            return RRSetVec4(1.0f, 1.0f, 1.0f, 1.0f);
        else
            return RRSetVec4(0.0f, 0.0f, 0.0f, 1.0f);
    }
    return RRSetVec4(1.0f, 1.0f, 1.0f, 1.0f);
}


RRVec4 RRQuadric::rrCalcGlare(RRVec3 N)
{
    float D;
    D = 2 * RRInnerProduct(tosrc, N);
    RRVec3 R;
    R = RRSetVec3(D * N.x - tosrc.x, D * N.y - tosrc.y, D * N.z - tosrc.z);
    RRRay r;
    r.rrSetSrc(cross);
    r.rrSetDir(R);

    RRPrim *p;
    float d, dmin;
    int it, index;
    dmin = 1.0e+10;
    it = primset->rrBegin();
    index = -1;
    while(it != primset->rrEnd()) {
        p = primset->rrGetPrim(it);
        d = p->rrHit(r);
        //printf("distance:%f\n", d);
        if(d < dmin && d > 0.0f) {
            dmin = d;
            index = it;
        }
        it++;
    }

    if(index != -1) {
        RRVec4 c;
        p = primset->rrGetPrim(index);
        c = p->rrTrace();
        return c;
    }
    else
        return RRSetVec4(0.0f, 0.0f, 0.0f, -1.0f);
}

RRVec4 RRQuadric::rrCalcTransparency(RRRay arg)
{
    RRVec4 c = RRSetVec4(0.0f, 0.0f, 0.0f, 1.0f);
    RRVec4 c_a = RRSetVec4(0.0f, 0.0f, 0.0f, 1.0f);

    RRPrim *p;
    float d, dmin;
    int it, index;
    dmin = 1.0e+8;
    it = primset->rrBegin();
    index = -1;
    while(it != primset->rrEnd()) {
        p = primset->rrGetPrim(it);
        d = p->rrHit(arg);
        //printf("distance:%f\n", d);
        if(d < dmin && d > 0.0f && this != p) {
            dmin = d;
            index = it;
        }
        it++;
    }

    p = primset->rrGetPrim(index);
    c = p->rrTrace();

    return c;
}


RRVec3 RRQuadric::rrCalcNorm()
{
    RRVec3 N;
    float sgn;
    if(C > 0.0f) {
        if(cross.z >= coord.z) sgn = 1.0f;
        else sgn = -1.0f;

        float Sec;
        Sec = sqrtf(C * (1 - A * (cross.x - coord.x) * (cross.x - coord.x) - B * (cross.y - coord.y) * (cross.y - coord.y)));
        float Nx = A * (cross.x - coord.x) / Sec;
        float Ny = B * (cross.y - coord.y) / Sec;
        N = RRSetVec3(Nx, Ny, sgn * 1.0);
        N = RRNormalize(N);
    }
    else if(B > 0.0f) {
        if(cross.y >= coord.y) sgn = 1.0f;
        else sgn = -1.0f;

        float Sec;
        Sec = sqrtf(B * (1 - A * (cross.x - coord.x) * (cross.x - coord.x) - C * (cross.z - coord.z) * (cross.z - coord.z)));
        float Nx = A * (cross.x - coord.x) / Sec;
        float Nz = C * (cross.z - coord.z) / Sec;
        N = RRSetVec3(Nx, sgn * 1.0, Nz);
        N = RRNormalize(N);
    }
    else if(A > 0.0f) {
        if(cross.z >= coord.z) sgn = 1.0f;
        else sgn = -1.0f;

        float Sec;
        Sec = sqrtf(A * (1 - B * (cross.y - coord.y) * (cross.y - coord.y) - C * (cross.z - coord.z) * (cross.z - coord.z)));
        float Ny = B *  (cross.y - coord.y) / Sec;
        float Nz = C * (cross.z - coord.z) / Sec;
        N = RRSetVec3(sgn * 1.0, Ny, Nz);
        N = RRNormalize(N);
    }
    else
        N = RRSetVec3(0.0f, 0.0f, 0.0f);

    return N;
}

RRVec4 RRQuadric::rrTrace()
{
    RRLight l;
    l = parent->rrGetLight();
    RRVec3 l_c = l.rrGetCoord();
    RRVec3 L;
    L = RRSetVec3(l_c.x - cross.x, l_c.y - cross.y, l_c.z - cross.z);
    L = RRNormalize(L);

    RRRay r;
    r.rrSetSrc(cross);
    r.rrSetDir(L);

    RRVec4 sh = RRSetVec4(1.0f, 1.0f, 1.0f, 1.0f);
    sh = rrCalcShadow(r);

    RRVec3 N;
    N = rrCalcNorm();

    float D;
    D = RRInnerProduct(N, L);
    D = (float)reversal * D;
    if(D < 0.0f) D = 0.0f;

    RRVec4 gl;
    if(reflection == true) {
        gl = rrCalcGlare(N);
        float w_temp = diff.w + sh.w + gl.w;
        float r_temp, g_temp, b_temp;
        r_temp = (diff.x * D * diff.w + sh.x * sh.w + gl.x * gl.w) / w_temp;
        g_temp = (diff.y * D * diff.w + sh.y * sh.w + gl.y * gl.w) / w_temp;
        b_temp = (diff.z * D * diff.w + sh.z * sh.w + gl.z * gl.w) / w_temp;
        return RRSetVec4(r_temp, g_temp, b_temp, 1.0f);
        //return gl;
    }
    else if(transparency == true) {
        RRVec3 e;
        e = RRSetVec3(-tosrc.x, -tosrc.y, -tosrc.z);
        r.rrSetDir(e);
        RRVec4 tr = rrCalcTransparency(r);

        float w_temp = diff.w + sh.w + tr.w;
        float r_temp, g_temp, b_temp;
        r_temp = (diff.x * D * diff.w + sh.x * sh.w + tr.x * tr.w) / w_temp;
        g_temp = (diff.y * D * diff.w + sh.y * sh.w + tr.y * tr.w) / w_temp;
        b_temp = (diff.z * D * diff.w + sh.z * sh.w + tr.z * tr.w) / w_temp;
        return RRSetVec4(r_temp, g_temp, b_temp, 1.0f);
    }
    else {
        float w_temp = diff.w + sh.w;
        float r_temp, g_temp, b_temp;
        r_temp = (diff.x * D * diff.w + sh.x * sh.w) / w_temp;
        g_temp = (diff.y * D * diff.w + sh.y * sh.w) / w_temp;
        b_temp = (diff.z * D * diff.w + sh.z * sh.w) / w_temp;
        return RRSetVec4(r_temp, g_temp, b_temp, 1.0f);
    }
}
